﻿using System;
using System.Xml.Linq;

namespace SharePoint.Orm.Caml {
	public class CamlComparisonOp : CamlExpr {
		private readonly CamlComparisonOpType _op;
		public CamlFieldRef FieldRef { get; set; }
		public CamlValue Value { get; set; }

		public CamlComparisonOpType Op {
			get { return _op; }
		}

		public CamlComparisonOp(CamlComparisonOpType op) {
			_op = op;
		}
        public CamlComparisonOp(CamlComparisonOpType op,CamlFieldRef fieldRef) : this(op) {
            FieldRef = fieldRef;
        }
		public CamlComparisonOp(CamlComparisonOpType op, CamlFieldRef fieldRef, CamlValue value)
			: this(op) {
			FieldRef = fieldRef;
			Value = value;
		}

		public CamlComparisonOp(CamlComparisonOpType op, string fieldName, CamlValueType type, string value) :
			this(op, new CamlFieldRef(fieldName), new CamlValue(type, value)) {
		}

		public override string ToString() {
			return ToXml().ToString();
		}

		public override XObject ToXml() {
            if(Value != null)
			    return new XElement(CompareOperatorString(_op), FieldRef.ToXml(), Value.ToXml());
		    return new XElement(CompareOperatorString(_op), FieldRef.ToXml());
		}

		private static string CompareOperatorString(CamlComparisonOpType ops) {
			switch (ops) {
				case CamlComparisonOpType.BeginsWith:
					return "BeginsWith";
				case CamlComparisonOpType.Contains:
					return "Contains";
				case CamlComparisonOpType.Equal:
					return "Eq";
				case CamlComparisonOpType.GreaterThanOrEquals:
					return "Geq";
				case CamlComparisonOpType.GreaterThan:
					return "Gt";
				case CamlComparisonOpType.IsNotNull:
					return "IsNotNull";
				case CamlComparisonOpType.IsNull:
					return "IsNull";
				case CamlComparisonOpType.LessThanOrEquals:
					return "Leq";
				case CamlComparisonOpType.LessThan:
					return "Lt";
				case CamlComparisonOpType.NotEquals:
					return "Neq";
				default:
					return "";
			}
		}

		public static bool IsComparisonOp(XElement element) {
			return ParseOp(element.Name.ToString()) != null;
		}

		public static CamlComparisonOp Parse(XElement element) {
			CamlComparisonOp result = new CamlComparisonOp(ParseOp(element.Name.ToString()).Value);
			int count = 0;
			foreach (XElement child in element.Elements()) {
				if (count == 0) {
					result.FieldRef = CamlFieldRef.Parse(child);
				} else if (count == 1) {
					result.Value = CamlValue.Parse(child);
				} else {
					throw new Exception("Unexpected element '" + child.Name + "'");
				}
				count++;
			}
			return result;
		}

		private static CamlComparisonOpType? ParseOp(string name) {
			switch (name) {
				case "BeginsWith":
					return CamlComparisonOpType.BeginsWith;
				case "Contains":
					return CamlComparisonOpType.Contains;
				case "Eq":
					return CamlComparisonOpType.Equal;
				case "Geq":
					return CamlComparisonOpType.GreaterThanOrEquals;
				case "Gt":
					return CamlComparisonOpType.GreaterThan;
				case "IsNotNull":
					return CamlComparisonOpType.IsNotNull;
				case "IsNull":
					return CamlComparisonOpType.IsNull;
				case "Leq":
					return CamlComparisonOpType.LessThanOrEquals;
				case "Lt":
					return CamlComparisonOpType.LessThan;
				case "Neq":
					return CamlComparisonOpType.NotEquals;
			}
			return null;
		}
	}

	public class CamlEquals : CamlComparisonOp {
		public CamlEquals()
			: base(CamlComparisonOpType.Equal) {
		}

		public CamlEquals(CamlFieldRef fieldRef, CamlValue value)
			: base(CamlComparisonOpType.Equal, fieldRef, value) {
		}

		public CamlEquals(string fieldName, CamlValueType type, string value)
			: base(CamlComparisonOpType.Equal, fieldName, type, value) {
		}
	}
    public class CamlNotEquals : CamlComparisonOp {
        public CamlNotEquals()
			: base(CamlComparisonOpType.NotEquals) {
		}

		public CamlNotEquals(CamlFieldRef fieldRef, CamlValue value)
			: base(CamlComparisonOpType.NotEquals, fieldRef, value) {
		}

		public CamlNotEquals(string fieldName, CamlValueType type, string value)
			: base(CamlComparisonOpType.NotEquals, fieldName, type, value) {
		}
    }
	public class CamlBeginsWith : CamlComparisonOp {
		public CamlBeginsWith()
			: base(CamlComparisonOpType.BeginsWith) {
		}

		public CamlBeginsWith(CamlFieldRef fieldRef, CamlValue value)
			: base(CamlComparisonOpType.BeginsWith, fieldRef, value) {
		}

		public CamlBeginsWith(string fieldName, CamlValueType type, string value)
			: base(CamlComparisonOpType.BeginsWith, fieldName, type, value) {
		}
	}
}
